// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System;
using System.Reflection;

namespace Microsoft.Extensions.Internal;

internal class ParameterDefaultValue
{
    private static readonly Type _nullable = typeof(Nullable<>);

    public static bool TryGetDefaultValue(ParameterInfo parameter, out object? defaultValue)
    {
        bool hasDefaultValue;
        var tryToGetDefaultValue = true;
        defaultValue = null;

        try
        {
            hasDefaultValue = parameter.HasDefaultValue;
        }
        catch (FormatException) when (parameter.ParameterType == typeof(DateTime))
        {
            // Workaround for https://github.com/dotnet/corefx/issues/12338
            // If HasDefaultValue throws FormatException for DateTime
            // we expect it to have default value
            hasDefaultValue = true;
            tryToGetDefaultValue = false;
        }

        if (hasDefaultValue)
        {
            if (tryToGetDefaultValue)
            {
                defaultValue = parameter.DefaultValue;
            }

            // Workaround for https://github.com/dotnet/corefx/issues/11797
            if (defaultValue == null && parameter.ParameterType.IsValueType)
            {
                defaultValue = Activator.CreateInstance(parameter.ParameterType);
            }

            // Handle nullable enums
            if (defaultValue != null &&
                parameter.ParameterType.IsGenericType &&
                parameter.ParameterType.GetGenericTypeDefinition() == _nullable
                )
            {
                var underlyingType = Nullable.GetUnderlyingType(parameter.ParameterType);
                if (underlyingType != null && underlyingType.IsEnum)
                {
                    defaultValue = Enum.ToObject(underlyingType, defaultValue);
                }
            }
        }

        return hasDefaultValue;
    }
}
